package util;

import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.sql.Time;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * util
 *
 * @description:
 * @author: yechen
 * @email: wangqingfeng@wxyundian.com
 * @date: 2020年09月27日 17:57
 */
@Slf4j
public class VideoCutUtil {

    /**
     * 获取视频时长，单位为秒
     *
     * @param file
     * @return 时长（s）
     */
    public static Long getVideoTime(File file) {
        Long times = 0L;
        try {
            FFmpegFrameGrabber ff = new FFmpegFrameGrabber(file);
            ff.start();
            times = ff.getLengthInTime() / (1000 * 1000);
            ff.stop();
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.info("======视频时长为{}s", times);
        return times;
    }

    /**
     * 截取视频中的某一段，生成新视频
     *
     * @param videoFile  源视频路径
     * @param outputFile 转换后的视频路径
     * @param startTime  开始抽取的时间点，单位:s
     * @param timeLength 需要抽取的时间段，单位:s；例如：该参数值为10时即抽取从startTime开始之后10秒的视频作为新视频
     */
    public static void cutVideo(File videoFile, String outputFile, Time startTime, int timeLength, String ffmpegExePath) {
        if (videoFile == null || !videoFile.exists()) {
            throw new RuntimeException("视频文件不存在：" + videoFile.getAbsolutePath());
        }
        if (null == outputFile) {
            throw new RuntimeException("转换后的视频路径为空，请检查转换后的视频存放路径是否正确");
        }
        Long duration = getVideoTime(videoFile);
        if (duration != null && startTime.getTime() + timeLength > duration) {
            throw new RuntimeException("截取时间不合法：" + startTime.toString() + "，因为截取时间大于视频的时长");
        }
        List<String> commond = new ArrayList<String>();
        commond.add("-ss");
        commond.add("0");
        commond.add("-t");
        commond.add("300");
        commond.add("-i");
        commond.add(videoFile.getAbsolutePath());
        commond.add("-vcodec");
        commond.add("copy");
        commond.add("-acodec");
        commond.add("copy");
        commond.add(outputFile);
        executeCommand(commond, ffmpegExePath);
    }

    /**
     * 执行FFmpeg命令
     *
     * @param commonds FFmpeg命令
     * @return FFmpeg执行命令过程中产生的各信息，执行出错时返回null
     */
    public static String executeCommand(List<String> commonds, String ffmpegPath) {
        if (CollectionUtils.isEmpty(commonds)) {
            log.error("--- 指令执行失败，因为要执行的FFmpeg指令为空！ ---");
        }
        LinkedList<String> ffmpegCmds = new LinkedList<>(commonds);
        ffmpegCmds.addFirst(ffmpegPath); // 设置ffmpeg程序所在路径

        //将标准输入流和错误输入流合并，通过标准输入流读取信息
        ProcessBuilder processBuilder = new ProcessBuilder();
        processBuilder.command(ffmpegCmds);
        processBuilder.redirectErrorStream(true);
        try {
            //启动进程
            Process start = processBuilder.start();
            //获取输入流
            InputStream inputStream = start.getInputStream();
            //转成字符输入流
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "gbk");
            int len = -1;
            char[] c = new char[1024];
            StringBuffer outputString = new StringBuffer();
            //读取进程输入流中的内容
            while ((len = inputStreamReader.read(c)) != -1) {
                String s = new String(c, 0, len);
                outputString.append(s);
                System.out.print(s);
            }
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    public static void main(String[] args) throws Exception {
        File file = new File("E:/video-version/course.mp4");
        String outputFile = "E:/video-version/course_cut1.mp4";
        String ffmpegPath = "D:/ffmpeg/bin/ffmpeg.exe";

        Long start = System.currentTimeMillis();
        VideoCutUtil.cutVideo(file, outputFile, new Time(0), 100, ffmpegPath);
        System.out.println("共花费" + (System.currentTimeMillis() - start) + "ms");

        //ffmpeg -i 001.mp4 -vf fps=0.1 -q:v 2 -f image2 D:\jietu\test-%d.jpg
        Long start1 = System.currentTimeMillis();
        System.out.println(VideoCutUtil.getVedioTime("E:/video-version/course.mp4", ffmpegPath));
        System.out.println("共花费" + (System.currentTimeMillis() - start1) + "ms");
    }

    /**
     * 获取视频总时间
     */
    public static Long getVedioTime(String vedioPath, String ffmpegPath) throws Exception {
        List<String> commands = new ArrayList<>();
        commands.add(ffmpegPath);
        commands.add("-i");
        commands.add(vedioPath);

        ProcessBuilder builder = new ProcessBuilder();
        builder.command(commands);
        Process p = builder.start();

        //从输入流中读取视频信息
        BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream()));
        StringBuilder stringBuilder = new StringBuilder();
        String line = "";
        while ((line = br.readLine()) != null) {
            stringBuilder.append(line);
        }
        br.close();

        //从视频信息中解析时长
        String regexDuration = "Duration: (.*?), start: (.*?), bitrate: (\\d*) kb\\/s";
        Pattern pattern = Pattern.compile(regexDuration);
        Matcher m = pattern.matcher(stringBuilder.toString());
        if (m.find()) {
            Long time = getTimelen(m.group(1));
            System.out.println("视频时长：" + time + "s , 开始时间：" + m.group(2) + ", 比特率：" + m.group(3) + "kb/s");
            return time;
        }
        return null;
    }

    private static Long getTimelen(String timelen) {
        Long min = 0L;
        String strs[] = timelen.split(":");
        if (strs[0].compareTo("0") > 0) {
            // 秒
            min += Long.parseLong(strs[0]) * 60 * 60;
        }
        if (strs[1].compareTo("0") > 0) {
            min += Long.parseLong(strs[1]) * 60;
        }
        if (strs[2].compareTo("0") > 0) {
            min += Math.round(Double.parseDouble(strs[2]));
        }
        return min;
    }
}
